/******************************************************************************* * Copyright (c) 2002, 2013 GEBIT Gesellschaft fuer EDV-Beratung * und Informatik-Technologien mbH, * Berlin, Duesseldorf, Frankfurt (Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation * IBM Corporation - bug fixes * John-Mason P. Shackelford - bug 40255 * Mark Melvin - bug 93378 *******************************************************************************/ package org.eclipse.ant.internal.ui.editor; import java.io.File; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; import org.eclipse.ant.internal.core.IAntCoreConstants; import org.eclipse.ant.internal.ui.AntUIPlugin; import org.eclipse.ant.internal.ui.AntUtil; import org.eclipse.ant.internal.ui.ExternalHyperlink; import org.eclipse.ant.internal.ui.IAntUIHelpContextIds; import org.eclipse.ant.internal.ui.IAntUIPreferenceConstants; import org.eclipse.ant.internal.ui.editor.actions.FoldingActionGroup; import org.eclipse.ant.internal.ui.editor.actions.RenameInFileAction; import org.eclipse.ant.internal.ui.editor.actions.RunToLineAdapter; import org.eclipse.ant.internal.ui.editor.actions.ToggleLineBreakpointAction; import org.eclipse.ant.internal.ui.editor.outline.AntEditorContentOutlinePage; import org.eclipse.ant.internal.ui.editor.text.AntEditorDocumentProvider; import org.eclipse.ant.internal.ui.editor.text.AntFoldingStructureProvider; import org.eclipse.ant.internal.ui.editor.text.IReconcilingParticipant; import org.eclipse.ant.internal.ui.editor.text.XMLTextHover; import org.eclipse.ant.internal.ui.model.AntElementNode; import org.eclipse.ant.internal.ui.model.AntModel; import org.eclipse.ant.internal.ui.model.AntModelChangeEvent; import org.eclipse.ant.internal.ui.model.AntModelCore; import org.eclipse.ant.internal.ui.model.AntProjectNode; import org.eclipse.ant.internal.ui.model.IAntElement; import org.eclipse.ant.internal.ui.model.IAntModelListener; import org.eclipse.ant.internal.ui.preferences.AntEditorPreferenceConstants; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.ui.actions.IRunToLineTarget; import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ISelectionValidator; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jface.text.source.projection.IProjectionListener; import org.eclipse.jface.text.source.projection.ProjectionSupport; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ShellAdapter; import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPartService; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.IShowInTargetList; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.views.contentoutline.ContentOutline; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; /** * The actual editor implementation for Eclipse's Ant integration. */ public class AntEditor extends TextEditor implements IReconcilingParticipant, IProjectionListener { /** * Updates the Ant outline page selection and this editor's range indicator. * * @since 3.0 */ private class EditorSelectionChangedListener implements ISelectionChangedListener { /** * Installs this selection changed listener with the given selection provider. If the selection provider is a post selection provider, post * selection changed events are the preferred choice, otherwise normal selection changed events are requested. * * @param selectionProvider */ public void install(ISelectionProvider selectionProvider) { if (selectionProvider == null || getAntModel() == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.addPostSelectionChangedListener(this); } else { selectionProvider.addSelectionChangedListener(this); } } /** * Removes this selection changed listener from the given selection provider. * * @param selectionProvider */ public void uninstall(ISelectionProvider selectionProvider) { if (selectionProvider == null || getAntModel() == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.removePostSelectionChangedListener(this); } else { selectionProvider.removeSelectionChangedListener(this); } } /* * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ @Override public void selectionChanged(SelectionChangedEvent event) { AntModel model = getAntModel(); ISelection selection = event.getSelection(); AntElementNode node = null; if (selection instanceof ITextSelection) { ITextSelection textSelection = (ITextSelection) selection; int offset = textSelection.getOffset(); node = model.getNode(offset, false); updateOccurrenceAnnotations(textSelection, model); } if (AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.OUTLINE_LINK_WITH_EDITOR)) { synchronizeOutlinePage(node, true); } setSelection(node, false); } } class StatusLineSourceViewer extends ProjectionViewer { public StatusLineSourceViewer(Composite composite, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, int styles) { super(composite, verticalRuler, overviewRuler, isOverviewRulerVisible(), styles); } /* * (non-Javadoc) * * @see org.eclipse.jface.text.ITextOperationTarget#doOperation(int) */ @Override public void doOperation(int operation) { if (getTextWidget() == null || !redraws()) { return; } switch (operation) { case CONTENTASSIST_PROPOSALS: String msg = fContentAssistant.showPossibleCompletions(); setStatusLineErrorMessage(msg); return; default: break; } super.doOperation(operation); } } /** * Finds and marks occurrence annotations. * * @since 3.1 */ class OccurrencesFinderJob extends Job { private IDocument fDocument; private ISelection fSelection; private ISelectionValidator fPostSelectionValidator; private boolean fCanceled = false; private IProgressMonitor fProgressMonitor; private List<Position> fPositions; public OccurrencesFinderJob(IDocument document, List<Position> positions, ISelection selection) { super("Occurrences Marker"); //$NON-NLS-1$ fDocument = document; fSelection = selection; fPositions = positions; if (getSelectionProvider() instanceof ISelectionValidator) fPostSelectionValidator = (ISelectionValidator) getSelectionProvider(); } // cannot use cancel() because it is declared final void doCancel() { fCanceled = true; cancel(); } private boolean isCanceled() { return fCanceled || fProgressMonitor.isCanceled() || fPostSelectionValidator != null && !(fPostSelectionValidator.isValid(fSelection) || fForcedMarkOccurrencesSelection == fSelection) || LinkedModeModel.hasInstalledModel(fDocument); } /* * @see Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus run(IProgressMonitor progressMonitor) { fProgressMonitor = progressMonitor; if (isCanceled()) return Status.CANCEL_STATUS; ITextViewer textViewer = getViewer(); if (textViewer == null) return Status.CANCEL_STATUS; IDocument document = textViewer.getDocument(); if (document == null) return Status.CANCEL_STATUS; IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider == null) return Status.CANCEL_STATUS; IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null) return Status.CANCEL_STATUS; // Add occurrence annotations int length = fPositions.size(); Map<Annotation, Position> annotationMap = new HashMap<>(length); for (int i = 0; i < length; i++) { if (isCanceled()) return Status.CANCEL_STATUS; String message; Position position = fPositions.get(i); // Create & add annotation try { message = document.get(position.offset, position.length); } catch (BadLocationException ex) { // Skip this match continue; } annotationMap.put(new Annotation("org.eclipse.jdt.ui.occurrences", false, message), //$NON-NLS-1$ position); } if (isCanceled()) { return Status.CANCEL_STATUS; } Object lock = getLockObject(document); if (lock == null) { updateAnnotations(annotationModel, annotationMap); } else { synchronized (lock) { updateAnnotations(annotationModel, annotationMap); } } return Status.OK_STATUS; } private void updateAnnotations(IAnnotationModel annotationModel, Map<Annotation, Position> annotationMap) { if (annotationModel instanceof IAnnotationModelExtension) { ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, annotationMap); } else { removeOccurrenceAnnotations(); Iterator<Map.Entry<Annotation, Position>> iter = annotationMap.entrySet().iterator(); while (iter.hasNext()) { Entry<Annotation, Position> mapEntry = iter.next(); annotationModel.addAnnotation(mapEntry.getKey(), mapEntry.getValue()); } } fOccurrenceAnnotations = annotationMap.keySet().toArray(new Annotation[annotationMap.keySet().size()]); } } /** * Cancels the occurrences finder job upon document changes. * * @since 3.1 */ class OccurrencesFinderJobCanceler implements IDocumentListener, ITextInputListener { public void install() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return; StyledText text = sourceViewer.getTextWidget(); if (text == null || text.isDisposed()) return; sourceViewer.addTextInputListener(this); IDocument document = sourceViewer.getDocument(); if (document != null) document.addDocumentListener(this); } public void uninstall() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) sourceViewer.removeTextInputListener(this); IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider != null) { IDocument document = documentProvider.getDocument(getEditorInput()); if (document != null) document.removeDocumentListener(this); } } /* * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ @Override public void documentAboutToBeChanged(DocumentEvent event) { if (fOccurrencesFinderJob != null) fOccurrencesFinderJob.doCancel(); } /* * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ @Override public void documentChanged(DocumentEvent event) { // do nothing } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, * org.eclipse.jface.text.IDocument) */ @Override public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { if (oldInput == null) return; oldInput.removeDocumentListener(this); } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ @Override public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (newInput == null) return; newInput.addDocumentListener(this); } } /** * Internal activation listener. * * @since 3.1 */ private class ActivationListener extends ShellAdapter { /* * @see org.eclipse.swt.events.ShellAdapter#shellActivated(org.eclipse.swt.events.ShellEvent) */ @Override public void shellActivated(ShellEvent e) { if (fMarkOccurrenceAnnotations && isActivePart()) { ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { fForcedMarkOccurrencesSelection = (ITextSelection) selection; updateOccurrenceAnnotations(fForcedMarkOccurrencesSelection, getAntModel()); } } } /* * @see org.eclipse.swt.events.ShellAdapter#shellDeactivated(org.eclipse.swt.events.ShellEvent) */ @Override public void shellDeactivated(ShellEvent e) { if (fMarkOccurrenceAnnotations && isActivePart()) removeOccurrenceAnnotations(); } } /** * Selection changed listener for the outline view. */ protected ISelectionChangedListener fSelectionChangedListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { fSelectionSetFromOutline = false; doSelectionChanged(event); fSelectionSetFromOutline = true; } }; private IAntModelListener fAntModelListener; /** * The page that shows the outline. */ protected AntEditorContentOutlinePage fOutlinePage; private boolean fInitialReconcile = true; /** * The editor selection changed listener. * * @since 3.0 */ private EditorSelectionChangedListener fEditorSelectionChangedListener; private ProjectionSupport fProjectionSupport; private AntFoldingStructureProvider fFoldingStructureProvider; private boolean fSelectionSetFromOutline = false; private FoldingActionGroup fFoldingGroup; /** * Holds the current occurrence annotations. * * @since 3.1 */ private Annotation[] fOccurrenceAnnotations = null; private OccurrencesFinderJob fOccurrencesFinderJob; private OccurrencesFinderJobCanceler fOccurrencesFinderJobCanceler; private ITextSelection fForcedMarkOccurrencesSelection; /** * The internal shell activation listener for updating occurrences. * * @since 3.1 */ private ActivationListener fActivationListener = new ActivationListener(); private boolean fMarkOccurrenceAnnotations; private boolean fStickyOccurrenceAnnotations; private AntModel fAntModel; /** * Default no-argument constructor */ public AntEditor() { setHelpContextId(IAntUIHelpContextIds.ANT_EDITOR); setRulerContextMenuId("#AntEditorRulerContext"); //$NON-NLS-1$ setEditorContextMenuId("#AntEditorContext"); //$NON-NLS-1$ configureInsertMode(SMART_INSERT, false); setInsertMode(INSERT); } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions() */ @Override protected void createActions() { super.createActions(); ResourceBundle bundle = ResourceBundle.getBundle("org.eclipse.ant.internal.ui.editor.AntEditorMessages"); //$NON-NLS-1$ IAction action = new TextOperationAction(bundle, "ContentFormat.", this, ISourceViewer.FORMAT); //$NON-NLS-1$ action.setActionDefinitionId(IJavaEditorActionDefinitionIds.FORMAT); setAction("ContentFormat", action); //$NON-NLS-1$ fFoldingGroup = new FoldingActionGroup(this, getViewer()); action = new RenameInFileAction(this); action.setActionDefinitionId("org.eclipse.ant.ui.renameInFile"); //$NON-NLS-1$ setAction("renameInFile", action); //$NON-NLS-1$ } /* * (non-Javadoc) * * @see org.eclipse.ui.editors.text.TextEditor#initializeEditor() Called from TextEditor.<init> */ @Override protected void initializeEditor() { setPreferenceStore(AntUIPlugin.getDefault().getCombinedPreferenceStore()); setCompatibilityMode(false); fMarkOccurrenceAnnotations = getPreferenceStore().getBoolean(AntEditorPreferenceConstants.EDITOR_MARK_OCCURRENCES); fStickyOccurrenceAnnotations = getPreferenceStore().getBoolean(AntEditorPreferenceConstants.EDITOR_STICKY_OCCURRENCES); setSourceViewerConfiguration(new AntEditorSourceViewerConfiguration(this)); setDocumentProvider(AntUIPlugin.getDefault().getDocumentProvider()); fAntModelListener = new IAntModelListener() { /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.editor.outline.IDocumentModelListener#documentModelChanged(org.eclipse.ant.internal.ui.editor.outline. * DocumentModelChangeEvent) */ @Override public void antModelChanged(AntModelChangeEvent event) { AntModel model = getAntModel(); if (event.getModel() == model) { if (event.isPreferenceChange()) { updateEditorImage(model); } if (fFoldingStructureProvider != null) { fFoldingStructureProvider.updateFoldingRegions(model); } } } }; AntModelCore.getDefault().addAntModelListener(fAntModelListener); } /* * (non-Javadoc) * * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> key) { if (key.equals(IContentOutlinePage.class)) { return (T) getOutlinePage(); } if (fProjectionSupport != null) { T adapter = fProjectionSupport.getAdapter(getSourceViewer(), key); if (adapter != null) { return adapter; } } if (key == IShowInTargetList.class) { return (T) new IShowInTargetList() { @Override public String[] getShowInTargetIds() { return new String[] { JavaUI.ID_PACKAGES, IPageLayout.ID_PROJECT_EXPLORER }; } }; } if (key == IToggleBreakpointsTarget.class) { return (T) new ToggleLineBreakpointAction(); } if (key == IRunToLineTarget.class) { return (T) new RunToLineAdapter(); } return super.getAdapter(key); } private AntEditorContentOutlinePage getOutlinePage() { if (fOutlinePage == null) { fOutlinePage = new AntEditorContentOutlinePage(AntModelCore.getDefault(), this); fOutlinePage.addPostSelectionChangedListener(fSelectionChangedListener); setOutlinePageInput(); } return fOutlinePage; } private void doSelectionChanged(SelectionChangedEvent selectionChangedEvent) { IStructuredSelection selection = (IStructuredSelection) selectionChangedEvent.getSelection(); if (!isActivePart() && AntUIPlugin.getActivePage() != null) { AntUIPlugin.getActivePage().bringToTop(this); } AntElementNode selectedXmlElement = (AntElementNode) selection.getFirstElement(); if (selectedXmlElement != null) { setSelection(selectedXmlElement, !isActivePart()); } } private boolean isActivePart() { IWorkbenchPart part = getActivePart(); return part != null && part.equals(this); } public void setSelection(AntElementNode reference, boolean moveCursor) { if (fSelectionSetFromOutline) { // the work has all just been done via a selection setting in the outline fSelectionSetFromOutline = false; return; } if (reference == null) { if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } return; } if (moveCursor) { markInNavigationHistory(); } while (reference.getImportNode() != null) { reference = reference.getImportNode(); } if (reference.isExternal()) { return; } ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return; } StyledText textWidget = sourceViewer.getTextWidget(); if (textWidget == null) { return; } try { int offset = reference.getOffset(); if (offset < 0) { return; } int length = reference.getSelectionLength(); int highLightLength = reference.getLength(); textWidget.setRedraw(false); if (highLightLength > 0) { setHighlightRange(offset, highLightLength, moveCursor); } if (!moveCursor) { return; } if (offset > -1 && length > 0) { sourceViewer.revealRange(offset, length); // Selected region begins one index after offset sourceViewer.setSelectedRange(offset, length); markInNavigationHistory(); } } catch (IllegalArgumentException x) { AntUIPlugin.log(x); } finally { textWidget.setRedraw(true); } } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractTextEditor#affectsTextPresentation(org.eclipse.jface.util.PropertyChangeEvent) */ @Override protected boolean affectsTextPresentation(PropertyChangeEvent event) { return ((AntEditorSourceViewerConfiguration) getSourceViewerConfiguration()).affectsTextPresentation(event); } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractTextEditor#handlePreferenceStoreChanged(org.eclipse.jface.util.PropertyChangeEvent) */ @Override protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { String property = event.getProperty(); if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { Object value = event.getNewValue(); StatusLineSourceViewer viewer = (StatusLineSourceViewer) getSourceViewer(); int newValue = -1; if (value instanceof Integer) { newValue = ((Integer) value).intValue(); } else if (value instanceof String) { newValue = Integer.parseInt((String) value); } if (newValue != -1) { viewer.getTextWidget().setTabs(newValue); } return; } if (AntEditorPreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) { boolean newBooleanValue = Boolean.valueOf(event.getNewValue().toString()).booleanValue(); if (newBooleanValue != fMarkOccurrenceAnnotations) { fMarkOccurrenceAnnotations = newBooleanValue; if (fMarkOccurrenceAnnotations) { installOccurrencesFinder(); } else { uninstallOccurrencesFinder(); } } return; } if (AntEditorPreferenceConstants.EDITOR_STICKY_OCCURRENCES.equals(property)) { boolean newBooleanValue = Boolean.valueOf(event.getNewValue().toString()).booleanValue(); fStickyOccurrenceAnnotations = newBooleanValue; return; } if (AntEditorPreferenceConstants.EDITOR_FOLDING_ENABLED.equals(property)) { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ProjectionViewer) { ProjectionViewer pv = (ProjectionViewer) sourceViewer; if (pv.isProjectionMode() != isFoldingEnabled()) { if (pv.canDoOperation(ProjectionViewer.TOGGLE)) { pv.doOperation(ProjectionViewer.TOGGLE); } } } return; } AntEditorSourceViewerConfiguration sourceViewerConfiguration = (AntEditorSourceViewerConfiguration) getSourceViewerConfiguration(); if (sourceViewerConfiguration != null) { if (affectsTextPresentation(event)) { sourceViewerConfiguration.adaptToPreferenceChange(event); } sourceViewerConfiguration.changeConfiguration(event); } super.handlePreferenceStoreChanged(event); } /* * @see org.eclipse.ui.editors.text.TextEditor#doSetInput(org.eclipse.ui.IEditorInput) */ @Override protected void doSetInput(IEditorInput input) throws CoreException { fAntModel = null; super.doSetInput(input); setOutlinePageInput(); if (fFoldingStructureProvider != null) { fFoldingStructureProvider.setDocument(getDocumentProvider().getDocument(input)); } } private void setOutlinePageInput() { if (fOutlinePage != null) { fOutlinePage.setPageInput(getAntModel()); } } /** * Returns the Ant model for the current editor input of this editor. * * @return the Ant model for this editor or <code>null</code> */ public AntModel getAntModel() { if (fAntModel == null) { IDocumentProvider provider = getDocumentProvider(); if (provider instanceof AntEditorDocumentProvider) { AntEditorDocumentProvider documentProvider = (AntEditorDocumentProvider) provider; fAntModel = documentProvider.getAntModel(getEditorInput()); } } return fAntModel; } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite, * org.eclipse.jface.text.source.IVerticalRuler, int) */ @Override protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { fOverviewRuler = createOverviewRuler(getSharedColors()); ISourceViewer viewer = new StatusLineSourceViewer(parent, ruler, getOverviewRuler(), styles); // ensure decoration support has been created and configured. getSourceViewerDecorationSupport(viewer); return viewer; } /** * Set the given message as error message to this editor's status line. * * @param msg * message to be set */ @Override protected void setStatusLineErrorMessage(String msg) { IEditorStatusLine statusLine = getAdapter(IEditorStatusLine.class); if (statusLine != null) statusLine.setMessage(true, msg, null); } public void openReferenceElement() { ISelection selection = getSelectionProvider().getSelection(); Object target = null; if (selection instanceof ITextSelection) { ITextSelection textSelection = (ITextSelection) selection; ISourceViewer viewer = getSourceViewer(); int textOffset = textSelection.getOffset(); IRegion region = XMLTextHover.getRegion(viewer, textOffset); target = findTarget(region); } openTarget(target); } protected void openTarget(Object node) { String errorMessage = null; if (node instanceof AntElementNode) { errorMessage = openNode((AntElementNode) node); if (errorMessage == null) { return; } } else if (node instanceof String) { errorMessage = openInEditor((String) node, getAntModel().getEditedFile()); if (errorMessage == null) { return; } } if (errorMessage == null || errorMessage.length() == 0) { errorMessage = AntEditorMessages.getString("AntEditor.3"); //$NON-NLS-1$ } setStatusLineErrorMessage(errorMessage); } /** * @param region * The region to find the navigation target * @return the navigation target at the specified region */ public Object findTarget(IRegion region) { ISourceViewer viewer = getSourceViewer(); IAntElement node = null; if (region != null) { IDocument document = viewer.getDocument(); String text = null; try { text = document.get(region.getOffset(), region.getLength()); } catch (BadLocationException e) { // do nothing } if (text != null && text.length() > 0) { AntModel model = getAntModel(); if (model == null) { return null; } node = model.getReferenceNode(text); if (node == null) { node = model.getTargetNode(text); if (node == null) { node = model.getPropertyNode(text); if (node == null) { String path = model.getPath(text, region.getOffset()); if (path != null) { path = model.getProjectNode().getProject().replaceProperties(path); return path; } node = model.getDefininingTaskNode(text); if (node == null) { node = model.getMacroDefAttributeNode(text); } } } } } } return node; } private String openNode(AntElementNode node) { String errorMessage = null; if (node.isExternal()) { String path = node.getFilePath(); errorMessage = openInEditor(path, null); } else { setSelection(node, true); } return errorMessage; } private String openInEditor(String path, File buildFile) { File buildFileParent = null; if (buildFile != null) { buildFileParent = buildFile.getParentFile(); } IFile file = AntUtil.getFileForLocation(path, buildFileParent); if (file != null && file.exists()) { try { IWorkbenchPage p = getEditorSite().getPage(); if (p != null) { IDE.openEditor(p, file, isActivePart()); } return null; } catch (PartInitException e) { return e.getLocalizedMessage(); } } File externalFile = new File(path); if (externalFile.exists()) { new ExternalHyperlink(externalFile, -1).linkActivated(); return null; } return IAntCoreConstants.EMPTY_STRING; } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager) */ @Override public void editorContextMenuAboutToShow(IMenuManager menu) { super.editorContextMenuAboutToShow(menu); if (getAntModel() != null) { IAction action = getAction("renameInFile"); //$NON-NLS-1$ menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, new Separator(ITextEditorActionConstants.GROUP_EDIT)); menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, action); action = getAction("ContentFormat"); //$NON-NLS-1$ menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, action); } } /* * (non-Javadoc) * * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl(Composite parent) { super.createPartControl(parent); ProjectionViewer projectionViewer = (ProjectionViewer) getSourceViewer(); createFoldingSupport(projectionViewer); if (isFoldingEnabled()) { projectionViewer.doOperation(ProjectionViewer.TOGGLE); } if (fMarkOccurrenceAnnotations) { installOccurrencesFinder(); } getEditorSite().getShell().addShellListener(fActivationListener); fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); } private void createFoldingSupport(ProjectionViewer projectionViewer) { fProjectionSupport = new ProjectionSupport(projectionViewer, getAnnotationAccess(), getSharedColors()); fProjectionSupport.setHoverControlCreator(new IInformationControlCreator() { @Override public IInformationControl createInformationControl(Shell shell) { return new AntSourceViewerInformationControl(shell); } }); fProjectionSupport.install(); ((ProjectionViewer) getViewer()).addProjectionListener(this); } private boolean isFoldingEnabled() { IPreferenceStore store = getPreferenceStore(); return store.getBoolean(AntEditorPreferenceConstants.EDITOR_FOLDING_ENABLED); } /* * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#isTabsToSpacesConversionEnabled() * * @since 3.3 */ @Override protected boolean isTabsToSpacesConversionEnabled() { return super.isTabsToSpacesConversionEnabled(); // provide package visibility } /* * (non-Javadoc) * * @see org.eclipse.ui.IWorkbenchPart#dispose() */ @Override public void dispose() { if (fEditorSelectionChangedListener != null) { fEditorSelectionChangedListener.uninstall(getSelectionProvider()); fEditorSelectionChangedListener = null; } ((ProjectionViewer) getViewer()).removeProjectionListener(this); if (fProjectionSupport != null) { fProjectionSupport.dispose(); fProjectionSupport = null; } uninstallOccurrencesFinder(); if (fActivationListener != null) { Shell shell = getEditorSite().getShell(); if (shell != null && !shell.isDisposed()) { shell.removeShellListener(fActivationListener); } fActivationListener = null; } AntModelCore.getDefault().removeAntModelListener(fAntModelListener); fAntModel = null; super.dispose(); } /* * (non-Javadoc) * * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) */ @Override public void doSave(IProgressMonitor monitor) { super.doSave(monitor); AntModel model = getAntModel(); model.updateMarkers(); updateEditorImage(model); } private void updateEditorImage(AntModel model) { Image titleImage = getTitleImage(); if (titleImage == null) { return; } AntProjectNode node = model.getProjectNode(); if (node != null) { postImageChange(node); } } private void updateForInitialReconcile() { IDocumentProvider provider = getDocumentProvider(); if (provider == null) {// disposed return; } AntModel antModel = getAntModel(); if (antModel == null) { return; } IDocument doc = provider.getDocument(getEditorInput()); if (doc == null) { return; // disposed } updateModelForInitialReconcile(doc, antModel); } private void updateModelForInitialReconcile(IDocument doc, AntModel model) { // must be outside of the lock, to avoid deadlocks, see bug 497276 updateEditorImage(model); // ensure to synchronize so that the AntModel is not nulled out underneath in the AntEditorDocumentProvider // when the editor/doc provider are disposed Object lock = getLockObject(doc); if (lock != null) { synchronized (lock) { fInitialReconcile = false; model.updateForInitialReconcile(); } } else { fInitialReconcile = false; model.updateForInitialReconcile(); } } private Object getLockObject(IDocument doc) { Object lock = null; if (doc instanceof ISynchronizable) { lock = ((ISynchronizable) doc).getLockObject(); } else { lock = getAntModel(); } return lock; } private void postImageChange(final AntElementNode node) { Shell shell = getSite().getShell(); if (shell != null && !shell.isDisposed()) { shell.getDisplay().asyncExec(new Runnable() { @Override public void run() { if (getSite().getShell() == null || getSite().getShell().isDisposed()) { return; } Image titleImage = getTitleImage(); Image newImage = node.getImage(); if (titleImage != newImage) { setTitleImage(newImage); } } }); } } public void synchronizeOutlinePage(boolean checkIfOutlinePageActive) { if (getSelectionProvider() == null) { return; } AntElementNode node = getNode(); synchronizeOutlinePage(node, checkIfOutlinePageActive); } protected void synchronize(boolean checkIfOutlinePageActive) { if (getSelectionProvider() == null) { return; } AntElementNode node = getNode(); if (AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.OUTLINE_LINK_WITH_EDITOR)) { synchronizeOutlinePage(node, checkIfOutlinePageActive); } setSelection(node, false); } private AntElementNode getNode() { AntModel model = getAntModel(); if (model == null) { return null; } AntElementNode node = null; ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { ITextSelection textSelection = (ITextSelection) selection; int offset = textSelection.getOffset(); node = model.getNode(offset, false); } return node; } protected void synchronizeOutlinePage(AntElementNode node, boolean checkIfOutlinePageActive) { if (fOutlinePage != null && !(checkIfOutlinePageActive && isAntOutlinePageActive())) { fOutlinePage.removePostSelectionChangedListener(fSelectionChangedListener); fOutlinePage.select(node); fOutlinePage.addPostSelectionChangedListener(fSelectionChangedListener); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.editor.text.IReconcilingParticipant#reconciled() */ @Override public void reconciled() { if (fInitialReconcile) { updateForInitialReconcile(); } SourceViewerConfiguration config = getSourceViewerConfiguration(); if (config == null) { return; // editor has been disposed. } IAutoEditStrategy[] strategies = config.getAutoEditStrategies(getViewer(), null); for (int i = 0; i < strategies.length; i++) { IAutoEditStrategy strategy = strategies[i]; if (strategy instanceof AntAutoEditStrategy) { ((AntAutoEditStrategy) strategy).reconciled(); } } Shell shell = getSite().getShell(); if (shell != null && !shell.isDisposed()) { shell.getDisplay().asyncExec(new Runnable() { @Override public void run() { if (getSite().getShell() == null || getSite().getShell().isDisposed()) { return; } synchronize(true); } }); } } private boolean isAntOutlinePageActive() { IWorkbenchPart part = getActivePart(); return part instanceof ContentOutline && ((ContentOutline) part).getCurrentPage() == fOutlinePage; } private IWorkbenchPart getActivePart() { IWorkbenchWindow window = getSite().getWorkbenchWindow(); IPartService service = window.getPartService(); return service.getActivePart(); } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractTextEditor#doSetSelection(org.eclipse.jface.viewers.ISelection) */ @Override protected void doSetSelection(ISelection selection) { super.doSetSelection(selection); synchronizeOutlinePage(true); } /** * Returns the viewer associated with this editor * * @return The viewer associated with this editor */ public ISourceViewer getViewer() { return getSourceViewer(); } protected FoldingActionGroup getFoldingActionGroup() { return fFoldingGroup; } /* * (non-Javadoc) * * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled() */ @Override public void projectionEnabled() { fFoldingStructureProvider = new AntFoldingStructureProvider(this); fFoldingStructureProvider.setDocument(getDocumentProvider().getDocument(getEditorInput())); fFoldingStructureProvider.updateFoldingRegions(getAntModel()); IPreferenceStore preferenceStore = AntUIPlugin.getDefault().getPreferenceStore(); preferenceStore.setValue(AntEditorPreferenceConstants.EDITOR_FOLDING_ENABLED, true); } /* * (non-Javadoc) * * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled() */ @Override public void projectionDisabled() { fFoldingStructureProvider = null; IPreferenceStore preferenceStore = AntUIPlugin.getDefault().getPreferenceStore(); preferenceStore.setValue(AntEditorPreferenceConstants.EDITOR_FOLDING_ENABLED, false); } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#initializeKeyBindingScopes() */ @Override protected void initializeKeyBindingScopes() { setKeyBindingScopes(new String[] { "org.eclipse.ant.ui.AntEditorScope" }); //$NON-NLS-1$ } protected IPreferenceStore getEditorPreferenceStore() { return getPreferenceStore(); } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#collectContextMenuPreferencePages() * * @since 3.1 */ @Override protected String[] collectContextMenuPreferencePages() { String[] ids = super.collectContextMenuPreferencePages(); String[] more = new String[ids.length + 6]; more[0] = "org.eclipse.ant.ui.AntEditorPreferencePage"; //$NON-NLS-1$ more[1] = "org.eclipse.ant.ui.AntCodeFormatterPreferencePage"; //$NON-NLS-1$ more[2] = "org.eclipse.ant.ui.AntCodeAssistPreferencePage"; //$NON-NLS-1$ more[3] = "org.eclipse.ant.ui.TemplatesPreferencePage"; //$NON-NLS-1$ more[4] = "org.eclipse.ant.ui.AntPreferencePage"; //$NON-NLS-1$ more[5] = "org.eclipse.ant.ui.AntRuntimePreferencePage"; //$NON-NLS-1$ System.arraycopy(ids, 0, more, 6, ids.length); return more; } /** * Updates the occurrences annotations based on the current selection. * * @param selection * the text selection * @param antModel * the model for the buildfile * @since 3.1 */ protected void updateOccurrenceAnnotations(ITextSelection selection, AntModel antModel) { if (fOccurrencesFinderJob != null) fOccurrencesFinderJob.cancel(); if (!fMarkOccurrenceAnnotations) { return; } if (selection == null || antModel == null) { return; } IDocument document = getSourceViewer().getDocument(); if (document == null) { return; } List<Position> positions = null; OccurrencesFinder finder = new OccurrencesFinder(this, antModel, document, selection.getOffset()); positions = finder.perform(); if (positions == null || positions.size() == 0) { if (!fStickyOccurrenceAnnotations) { removeOccurrenceAnnotations(); } return; } fOccurrencesFinderJob = new OccurrencesFinderJob(document, positions, selection); fOccurrencesFinderJob.run(new NullProgressMonitor()); } private void removeOccurrenceAnnotations() { IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider == null) { return; } IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null || fOccurrenceAnnotations == null) { return; } IDocument document = documentProvider.getDocument(getEditorInput()); Object lock = getLockObject(document); if (lock == null) { updateAnnotationModelForRemoves(annotationModel); } else { synchronized (lock) { updateAnnotationModelForRemoves(annotationModel); } } } private void updateAnnotationModelForRemoves(IAnnotationModel annotationModel) { if (annotationModel instanceof IAnnotationModelExtension) { ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, null); } else { for (int i = 0, length = fOccurrenceAnnotations.length; i < length; i++) { annotationModel.removeAnnotation(fOccurrenceAnnotations[i]); } } fOccurrenceAnnotations = null; } protected void installOccurrencesFinder() { fMarkOccurrenceAnnotations = true; if (getSelectionProvider() != null) { ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { fForcedMarkOccurrencesSelection = (ITextSelection) selection; updateOccurrenceAnnotations(fForcedMarkOccurrencesSelection, getAntModel()); } } if (fOccurrencesFinderJobCanceler == null) { fOccurrencesFinderJobCanceler = new OccurrencesFinderJobCanceler(); fOccurrencesFinderJobCanceler.install(); } } protected void uninstallOccurrencesFinder() { fMarkOccurrenceAnnotations = false; if (fOccurrencesFinderJob != null) { fOccurrencesFinderJob.cancel(); fOccurrencesFinderJob = null; } if (fOccurrencesFinderJobCanceler != null) { fOccurrencesFinderJobCanceler.uninstall(); fOccurrencesFinderJobCanceler = null; } removeOccurrenceAnnotations(); } public boolean isMarkingOccurrences() { return fMarkOccurrenceAnnotations; } /** * The editor has entered or exited linked mode. * * @param inLinkedMode * whether an enter or exit has occurred * @param affectsOccurrences * whether to change the state of the occurrences finder */ public void setInLinkedMode(boolean inLinkedMode, boolean affectsOccurrences) { if (inLinkedMode) { getAntModel().setShouldReconcile(false); if (affectsOccurrences) { uninstallOccurrencesFinder(); } } else { getAntModel().setShouldReconcile(true); if (affectsOccurrences) { installOccurrencesFinder(); } } } }